package com.yss.sofa.report.engine;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig.Feature;

import com.yss.sofa.report.engine.cell.page.PageCell;
import com.yss.sofa.report.engine.cell.visible.VisibleCell;
import com.yss.sofa.report.engine.cell.visible.result.ResultCell;
import com.yss.sofa.report.engine.core.Block;
import com.yss.sofa.report.engine.core.Matrix;

public class Report implements Serializable{
	private static final long serialVersionUID = 8263462022229668121L;
	private static byte[] copy(byte[] source){
		byte[] data=new byte[source.length];
		System.arraycopy(source, 0, data, 0, source.length);
		return data;
	}

	private Workbook toExcel(byte[] middle) throws Exception {
		InputStream in = new ByteArrayInputStream(middle);
		boolean isXssWorkbook = EngineUtil.hasOOXMLHeader(in);
		
		if (isXssWorkbook) {
			try {
				Workbook excel07 = new XSSFWorkbook(in);
				return excel07;
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				in.close();
			}
		} else {
			try {
				Workbook excel03 = new HSSFWorkbook(in);
				return excel03;
			}catch (Exception e) {
				e.printStackTrace();
			} finally {
				in.close();
			}
		}
		return null;
	}
	
	static byte[] toMiddle(Workbook excel) throws Exception{
		ByteArrayOutputStream out=new ByteArrayOutputStream();
		try {
			excel.write(out);
			out.flush();
			return out.toByteArray();
		} finally {
			out.close();
		}
	}
	private transient Workbook excel=null;
	private String output;//==main or ==form
	private byte[] middle;
	private Map<String,PageCell> pages=new LinkedHashMap<String,PageCell>();//key=report.sheet
	//模板里面的单元格列表
	private Map<String,Set<VisibleCell>> cells=new LinkedHashMap<String, Set<VisibleCell>>();//key=report.sheet
	private Map<String, DataSetData> datas=new HashMap<String, DataSetData>();
	private Map<String, Serializable> params=new HashMap<String, Serializable>();
	private Map<String, String> rawParams=new HashMap<String, String>();
	private Map<String, String> consts=new HashMap<String, String>();
	private Map<String, Map<ResultCell,byte[]>> images=new LinkedHashMap<String,Map<ResultCell, byte[]>>();
	private Map<String,Matrix> matrixs=new LinkedHashMap<String, Matrix>();//key=report.sheet
	private LinkedHashMap<String,String> sheets=new LinkedHashMap<String, String>();//key=template.sheet,value=report.sheet
	private List<String> tos=new ArrayList<String>();
	private TemplateCustom customTemplate;
	private String customTemplateType;
	private String exportName;
	
	private Map<String, String> formDefaultParams=new HashMap<String, String>();
	
	//表示当选择了sofa配置的地址时候，选择的服务名称
	private String externalServiceName; 
	
	//为空表示没有选择推送外部不服务，1表示调用外部接口，2表示调用sofa的配置地址,3表示自动任务调用，先查接口，再查sofa常量配置
	private String externalServiceSelectMode;
	
	//导出路径，
	private String exportPath;
	
	private Report(){
		super();
	}
	public Report(byte[] middle,Map<String,DataSetData> datas,Map<String,Serializable> params,Map<String,String> rawParams,Map<String,String> consts,String output){
		this();
		this.middle=copy(middle);
		this.params=params;
		this.output=output;
		this.datas=datas;
		this.rawParams.putAll(rawParams);
		this.consts.putAll(consts);
	}
	public void print(){
		for(String to:matrixs.keySet()){
			System.out.println(this.pages.get(to).getFrom()+" -> "+to);
			matrixs.get(to).print();
		}
	}
	public void pack(String to,List<Entry<String, Integer>> dataSetsRows,Map<String,Integer> formulaDataSetNumMap){
		matrixs.get(to).pack(dataSetsRows,formulaDataSetNumMap);
	}
	public Report add(PageCell page,Set<VisibleCell> cells) throws Exception{
		String from=page.getFrom();
		String to=page.getName();
		if(this.sheets.containsValue(to)){
			String otherFrom=this.pages.get(to).getFrom();
			throw new Exception("sheet["+from+"]的转换结果为sheet["+to+"]，与sheet["+otherFrom+"]的转换结果重复");
		}
		this.pages.put(to, page);
		this.cells.put(to, cells);
		this.matrixs.put(to, new Matrix());
		this.images.put(to, new HashMap<ResultCell, byte[]>());
		this.sheets.put(from, to);
		this.tos.add(to);
		return this;
	}
	public String getReportName() {
		return params.get(Const.SYS_PARAMS_REPORT_NAME).toString();
	}
	public String getReportCode() {
		return params.get(Const.SYS_PARAMS_REPORT_CODE).toString();
	}
	public void append(String to,Block block) throws Exception{
		matrixs.get(to).set(block);
	}
	public int getRowCount(String to){
		return matrixs.get(to).getRowCount();
	}
	public int getColCount(String to){
		return matrixs.get(to).getColCount();
	}
	public ResultCell getCell(String to,int row,int col){
		return matrixs.get(to).get(row, col);
	}
	public byte[] getMiddle() {
		return middle;
	}
	public Map<String, Serializable> getParams() {
		return params;
	}
	public String getOutput() {
		return output;
	}
	public Map<ResultCell,byte[]> getImages(String to) {
		return images.get(to);
	}
	public Map<String, DataSetData> getDatas() {
		return datas;
	}
	public boolean isEmptyReportSheet(String to){
		return this.matrixs.get(to).isEmpty();
	}
	public PageCell getPage(String to) {
		return pages.get(to);
	}
	public String getRenderer(String to) {
		return pages.get(to).getRenderer();
	}
	public Set<String> getFromSheetNames() {
		return this.sheets.keySet();
	}
	public Collection<String> getToSheetNames() {
		return this.sheets.values();
	}
	public LinkedHashMap<String,String> getSheetMapping(){
		return this.sheets;
	}
	public Set<VisibleCell> getCells(String to){
		return cells.get(to);
	}
	public void doForceMerge(String to){
		this.matrixs.get(to).doForceMerge();
	}
	public int getPageIndex(){
		return (Integer)(params.get("PAGE_INDEX"));
	}
	public int getPageCount(){
		return pages.get(sheets.get(output)).getPageCount();
	}
	
	public int getPageTotal(){
		return pages.get(sheets.get(output)).getPageTotal();
	}
	public Map<String, String> getRawParams() {
		return rawParams;
	}
	public Map<String, String> getConsts() {
		return consts;
	}
	public Set<ResultCell> getRanges(String to){
		Set<ResultCell> result=new HashSet<ResultCell>();
		Matrix matrix=matrixs.get(to);
		for(int r=0;r<matrix.getRowCount();r++){
			for(int c=0;c<matrix.getColCount();c++){
				ResultCell cell=matrix.get(r, c);
				if(cell!=null && cell.isMerge() && !result.contains(cell)){
					result.add(cell);
				}
			}
		}
		return result;
	}
	public boolean isColAutoSize(String to,int col){
		Matrix matrix=matrixs.get(to);
		int count=matrix.getRowCount();
		for(int i=0;i<count;i++){
			ResultCell rc=matrix.get(i, col);
			if(rc!=null && rc.isColAutoSize(col, pages.get(to))){
				return true;
			}
		}
		return false;
	}
	public boolean isRowAutoSize(String to,int row){
		Matrix matrix=matrixs.get(to);
		int count=matrix.getColCount();
		for(int i=0;i<count;i++){
			ResultCell rc=matrix.get(row, i);
			if(rc!=null && rc.isRowAutoSize(row, pages.get(to))){
				return true;
			}
		}
		return false;
	}
	public String toLog(){
		try {
			Map<String,Object> log=new LinkedHashMap<String, Object>();
			log.put("report", rawParams.get(Const.SYS_PARAMS_REPORT_CODE));
			log.put("output", output);
			log.put("params", rawParams);
			log.put("consts", consts);
			ObjectMapper mapper=new ObjectMapper();
			mapper.configure(Feature.INDENT_OUTPUT, false);
			return mapper.writeValueAsString(log);
		} catch (Exception e) {
			return "<"+this.toString()+">";
		}
	}
	public Workbook getExcel(boolean copy) throws Exception{
		if(copy){
			return toExcel(middle);
		} else {
			if(this.excel==null){
				this.excel=toExcel(middle);
			}
			return excel;
		}
	}
	public void updateMiddle(Workbook excel) throws Exception{
		this.middle=toMiddle(excel);
		this.excel=excel;
	}
	public void closeExcel() throws IOException{
		if(this.excel!=null){
			this.excel.close();
			this.excel=null;
		}
	}
	public String getToName(int index){
		return this.tos.get(index);
	}
	public int getToIndex(String to){
		return this.tos.indexOf(to);
	}
	public TemplateCustom getCustomTemplate() {
		return customTemplate;
	}
	public void setCustomTemplate(TemplateCustom customTemplate) {
		this.customTemplate = customTemplate;
	}

	public String getCustomTemplateType() {
		return customTemplateType;
	}

	public void setCustomTemplateType(String customTemplateType) {
		this.customTemplateType = customTemplateType;
	}

	public Map<String, Matrix> getMatrixs() {
		return matrixs;
	}

	public void setMatrixs(Map<String, Matrix> matrixs) {
		this.matrixs = matrixs;
	}

	public String getExportName() {
		return exportName;
	}

	public void setExportName(String exportName) {
		this.exportName = exportName;
	}

	public String getExternalServiceName() {
		return externalServiceName;
	}

	public void setExternalServiceName(String externalServiceName) {
		this.externalServiceName = externalServiceName;
	}

	public String getExternalServiceSelectMode() {
		return externalServiceSelectMode;
	}

	public void setExternalServiceSelectMode(String externalServiceSelectMode) {
		this.externalServiceSelectMode = externalServiceSelectMode;
	}

	public String getExportPath() {
		return exportPath;
	}

	public void setExportPath(String exportPath) {
		this.exportPath = exportPath;
	}

	public Map<String, String> getFormDefaultParams() {
		return formDefaultParams;
	}

	public void setFormDefaultParams(Map<String, String> formDefaultParams) {
		this.formDefaultParams = formDefaultParams;
	}
	
}
